goog.provide('mp.graphics.Element');
//goog.exportSymbol('mp.graphics.Element', mp.graphics.Element);

goog.require('goog.graphics');
goog.require('goog.graphics.ext');
goog.require('goog.math.Coordinate');
goog.require('goog.math.Size');
goog.require('mp.tools.Scheme');



/**
 * Wrapper for a graphics group.
 * @param {Object} model Data model for parsing.
 * @param {mp.graphics.Graphics} graphics Graphics canvas object.
 * @param {goog.graphics.GroupElement=} opt_wrapper The thin wrapper
 *     to wrap. If omitted, a new group will be created. Must be included
 *     when graphics is null.
 * @constructor
 * @extends {goog.graphics.ext.Group}
 */
mp.graphics.Element = function(model, graphics, opt_wrapper) {
  goog.base(this, graphics.mainLayer_, opt_wrapper);

  this.graphics_ = graphics;
  this.captionGroup_ = new goog.graphics.ext.Group(graphics.captionLayer_);
  this.placeholderGroup_ = new goog.graphics.ext.Group(graphics.placeholderLayer_);
  this.bgGroup_ = new goog.graphics.ext.Group(graphics.bgLayer_);

  this.parseModel_(model);
  this.createDomInternal_();
};
goog.inherits(mp.graphics.Element, goog.graphics.ext.Group);


/**
 * @type {mp.graphics.Graphics}
 * @private
 */
mp.graphics.Element.prototype.graphics_ = null;


/**
 * @type {goog.graphics.ext.Element}
 * @private
 */
mp.graphics.Element.prototype.mainObject_;


/**
 * @type {Array.<goog.graphics.ext.Group>|undefined}
 * @private
 */
mp.graphics.Element.prototype.places_;

/**
 * @type {Array.<goog.graphics.ext.Group>|undefined}
 * @private
 */
mp.graphics.Element.prototype.labels_;

/**
 * @type {Array.<goog.graphics.ext.Group>|undefined}
 * @private
 */
mp.graphics.Element.prototype.placesCaptions_;


/**
 * @type {goog.graphics.ext.Group}
 * @private
 */
mp.graphics.Element.prototype.captionGroup_;


/**
 * @type {goog.graphics.ext.Group}
 * @private
 */
mp.graphics.Element.prototype.placeholderGroup_;


/**
 * @type {goog.graphics.ext.Group}
 * @private
 */
mp.graphics.Element.prototype.bgGroup_;


/**
 * @type {string|undefined}
 * @private
 */
mp.graphics.Element.prototype.id_;


/**
 * @type {number}
 * @private
 */
mp.graphics.Element.prototype.placeSize_ = 54;


/**
 * @type {goog.math.Coordinate|undefined}
 * @private
 */
mp.graphics.Element.prototype.position_;


/**
 * @type {goog.math.Size|undefined}
 * @private
 */
mp.graphics.Element.prototype.size_;


/**
 * @type {mp.tools.Scheme|undefined}
 * @private
 */
mp.graphics.Element.prototype.scheme_;


/**
 * @type {Array.<Object>|undefined}
 * @private
 */
mp.graphics.Element.prototype.sits_;


/**
 * @type {string}
 * @private
 */
mp.graphics.Element.prototype.title_ = '';


/**
 * @type {boolean}
 * @private
 */
mp.graphics.Element.prototype.isRound_ = false;


/**
 * @type {boolean}
 * @private
 */
mp.graphics.Element.prototype.isTable_ = false;


/**
 * @type {string}
 * @private
 */
mp.graphics.Element.prototype.pattern_ = '#93B9D1';


/**
 * @type {string}
 * @private
 */
mp.graphics.Element.prototype.stroke_ = '#DBEDBE';


/**
 * @param {object} model Data model for parsing.
 * @private
 */
mp.graphics.Element.prototype.parseModel_ = function(model) {
  this.title_ = !! model['title'] ? model['title'] : '';
  this.isRound_ = !! model['isRound'];
  this.isTable_ = !! model['isTable'];
  this.pattern_ = !! model['pattern'] ? model['pattern'] : this.pattern_;
  if (goog.isDef(model['position']))
    this.position_ = new goog.math.Coordinate(
        model['position']['left'],
        model['position']['top']);
  if (goog.isDef(model['size']))
    this.size_ = new goog.math.Size(model.size.width, model.size.height);
  if (goog.isDef(model['scheme']))
    this.scheme_ = model['scheme'];
  if (goog.isArray(this.scheme_)) {
    this.scheme_ = new mp.tools.Scheme(this.scheme_);
    this.scheme_.reflectNonEmpty(mp.tools.SchemePlace.RESIZER);
  }
  this.sits_ = !! model['sits'] && goog.isArray(model['sits']) ? model['sits'] : [];
};


/**
 * @return {object} Data model.
 */
mp.graphics.Element.prototype.serializeModel = function() {
  var model = {};
  model['title'] = this.title_;
  model['isRound'] = this.isRound_;
  model['isTable'] = this.isTable_;
  model['pattern'] = this.pattern_;
  model['position'] = {'left': this.position_.x, 'top': this.position_.y};
  model['size'] = this.size_;
  model['scheme'] = this.scheme_;
  if (this.scheme_ instanceof mp.tools.Matrix)
    model['scheme'] = this.scheme_.toArray();
  model['sits'] = this.sits_;
  return JSON.parse(JSON.stringify(model));
};


/**
 * @return {goog.math.Coordinate} Position.
 */
mp.graphics.Element.prototype.getPosition = function() {
  return new goog.math.Coordinate(this.getLeft(), this.getTop());
};

/** @override */
mp.graphics.Element.prototype.setPosition = function(left, top, opt_chain) {
  goog.base(this, 'setPosition', left, top, opt_chain);
  this.captionGroup_.setPosition(left, top, opt_chain);
  this.placeholderGroup_.setPosition(left, top, opt_chain);
  this.bgGroup_.setPosition(left, top, opt_chain);
};

/** @override */
mp.graphics.Element.prototype.setRotation = function(angle) {
  var curAng = (this.getRotation() + angle) % 360;
  goog.base(this, 'setRotation', curAng);
  this.captionGroup_.setRotation(curAng);
  this.placeholderGroup_.setRotation(curAng);
  this.bgGroup_.setRotation(curAng);
  
  var pos = this.getPosition();
  var w2 = this.mainObject_.getWidth() / 2;
  var h2 = this.mainObject_.getHeight() / 2;
  var r = Math.sqrt(w2*w2 + h2*h2);
  var ang = Math.atan(w2/h2);
  var nx = r*Math.cos(ang+curAng);
  var ny = r*Math.sin(ang+curAng);
  var dx = nx - w2;
  var dy = ny - h2;

  this.setPosition(
      pos.x + dx,
      pos.y + dy
  );
}

/**
 * Returns the underlying object.
 * @return {Element} The underlying element.
 */
mp.graphics.Element.prototype.getElement = function() {
  return this.getWrapper().getElement();
};


/**
 * Resize scheme.
 * @param {number|goog.math.Size} size New scheme size.
 */
mp.graphics.Element.prototype.resizeScheme = function(size) {
  if (goog.isNumber(size)) {
    this.resizeScheme(new goog.math.Size(size, size));
    return;
  }

  if (this.scheme_ instanceof mp.tools.Scheme) {
    this.scheme_.resizeScheme(size.width, false);
    this.scheme_.resizeScheme(size.height, true);
  } else if (size.width >= 2) {
    this.scheme_ = size.width;
  }
};


/**
 * Rotate scheme 90deg.
 */
mp.graphics.Element.prototype.rotateScheme = function() {
  if (!(this.scheme_ instanceof mp.tools.Scheme))
    return;

  this.scheme_.rotate();

  // Process sits
  var len = this.sits_.length;
  for (var i = 0; i < len; i++) {
    if (this.sits_[i] != null) {
      var position = {
        y: this.sits_[i].position.x,
        x: this.scheme_.width() - 1 - this.sits_[i].position.y
      };

      this.sits_[i].position.x = position.x;
      this.sits_[i].position.y = position.y;
    }
  }
};


/**
 * Create dom internal.
 */
mp.graphics.Element.prototype.createDomInternal_ = function() {
  if (this.isTable_) {
    // Process table
    if (this.isRound_) {
      var figure = this.createRoundTableDom();
    } else {
      var figure = this.createGeneralTableDom();
    }
    this.createPlacesDom();
    this.createSitsDom();
  } else {
    // Process simple object
    if (this.isRound_) {
      var figure = new goog.graphics.ext.Ellipse(this);
    } else {
      var figure = new goog.graphics.ext.Rectangle(this);
    }
    this.mainObject_ = figure;

    if (!! this.size_)
      figure.setSize(this.size_.width, this.size_.height);
    else if (this.scheme_ instanceof mp.tools.Matrix)
      figure.setSize(
          this.scheme_.width() * this.placeSize_,
          this.scheme_.height() * this.placeSize_);
    
    this.pattern_ = "#DEDA66";
    this.stroke_ = null;
  }
  if(!! this.pattern_)
    figure.setFill(new goog.graphics.SolidFill(this.pattern_));
  if(!! this.stroke_)
    figure.setStroke(new goog.graphics.Stroke(1, this.stroke_));

  if (!! this.position_) {
    if(!this.isRound_ && this.isTable_) {
      var bounds = this.getMainObjectRect();
      this.setPosition(this.position_.x - bounds.left, this.position_.y - bounds.top);
    } else {
      this.setPosition(this.position_.x, this.position_.y);
    }
  }

  this.createCaptionsDom();
};


/**
 * @return {goog.graphics.ext.Element} Table graphic object.
 */
mp.graphics.Element.prototype.createGeneralTableDom = function() {
  var paths = this.scheme_.getPaths(mp.tools.SchemePlace.TABLE),
      margin = 2,
      lineLen = this.placeSize_ + margin * 2;

  var path = new goog.graphics.ext.Path();
  for (var i = 0; i < paths.length; i++) {
    var line = paths[i][0];
    path.moveTo(
        line.x0 * lineLen,
        line.y0 * lineLen).
        lineTo(
        line.x1 * lineLen,
        line.y1 * lineLen);
    for (var j = 1; j < paths[i].length; j++) {
      line = paths[i][j];
      path.lineTo(line.x1 * lineLen, line.y1 * lineLen);
    }
  }
  path.close();

  var table = new goog.graphics.ext.Shape(this, path);
  this.mainObject_ = table;

  // create places
  var places = [];
  var cells = this.scheme_.getPlaceholders();
  for (var i = cells.length - 1; i >= 0; i--) {
    var place = new goog.graphics.ext.Group(this.placeholderGroup_);
    place.setSize(this.placeSize_, this.placeSize_);
    place.setPosition(
        cells[i].x * lineLen + margin,
        cells[i].y * lineLen + margin);
    place.schemePosition = cells[i].clone();
    places.push(place);
  }
  this.places_ = places;

  return table;
};


/**
 * @return {goog.graphics.ext.Element} Table graphic object.
 */
mp.graphics.Element.prototype.createRoundTableDom = function() {
  var table = new goog.graphics.ext.Ellipse(this);

  var horde = (this.placeSize_ + 4);
  var ang = 2 * Math.PI / this.scheme_;

  var size = horde / Math.sin(ang / 2) - this.placeSize_;
  size = Math.max(this.placeSize_ * 3 / 2, size);

  table.setSize(size, size);
  this.mainObject_ = table;

  // create places
  var places = [];
  var r = (size + this.placeSize_) / 2 + 4;
  var curAngle = 0;
  var count = this.scheme_;
  var centerOffset = (size - this.placeSize_) / 2;
  do {
    var x = r * Math.cos(curAngle) + centerOffset;
    var y = r * Math.sin(curAngle) + centerOffset;

    var place = new goog.graphics.ext.Group(this.placeholderGroup_);
    place.setSize(this.placeSize_, this.placeSize_);
    place.setPosition(x, y);
    places.push(place);

    curAngle += ang;
    count--;
  } while (count > 0);

  this.places_ = places;

  return table;
};


/**
 *
 */
mp.graphics.Element.prototype.createPlacesDom = function() {
  this.placesCaptions_ = [];
  for (var idx = 0; idx < this.places_.length; idx++) {
    var place = this.places_[idx];

    if (this.isRound_) {
      var step = 360 / this.scheme_,
          ang = step * idx,
          isRtl = ang >= 90 && ang < 270,
          delta = isRtl ? 180 : 0;
      place.setRotation(this.scheme_ > 5 ? delta + ang : 0);
      place.isRtl = isRtl;
      var placeFigure = new goog.graphics.ext.Ellipse(place);
    } else {
      var pos = place.schemePosition;
      var tpos = pos.clone();
      var rpos = pos.clone();
      var bpos = pos.clone();
      var lpos = pos.clone();
      tpos.y--;
      rpos.x++;
      bpos.y++;
      lpos.x--;

      var neighb = {
        t: mp.tools.SchemePlace.EMPTY,
        r: mp.tools.SchemePlace.EMPTY,
        b: mp.tools.SchemePlace.EMPTY,
        l: mp.tools.SchemePlace.EMPTY
      };

      var sphs = this.scheme_.getPlaceholders();
      for (var i = sphs.length - 1; i >= 0; i--) {
        if (goog.math.Coordinate.equals(sphs[i], tpos)) neighb.t = mp.tools.SchemePlace.PLACE;
        else if (goog.math.Coordinate.equals(sphs[i], rpos)) neighb.r = mp.tools.SchemePlace.PLACE;
        else if (goog.math.Coordinate.equals(sphs[i], bpos)) neighb.b = mp.tools.SchemePlace.PLACE;
        else if (goog.math.Coordinate.equals(sphs[i], lpos)) neighb.l = mp.tools.SchemePlace.PLACE;
      }

      if (this.scheme_.getValueAt(tpos.y, tpos.x) == mp.tools.SchemePlace.TABLE) neighb.t = mp.tools.SchemePlace.TABLE;
      else if (this.scheme_.getValueAt(rpos.y, rpos.x) == mp.tools.SchemePlace.TABLE) neighb.r = mp.tools.SchemePlace.TABLE;
      else if (this.scheme_.getValueAt(bpos.y, bpos.x) == mp.tools.SchemePlace.TABLE) neighb.b = mp.tools.SchemePlace.TABLE;
      else if (this.scheme_.getValueAt(lpos.y, lpos.x) == mp.tools.SchemePlace.TABLE) neighb.l = mp.tools.SchemePlace.TABLE;

      var ang = 0;
      var isRtl = false;

      if (neighb.t == mp.tools.SchemePlace.TABLE) {
        ang = -90;
        isRtl = true;
      }
      if (neighb.b == mp.tools.SchemePlace.TABLE) {
        ang = -90;
      }
      if (neighb.r == mp.tools.SchemePlace.TABLE) {
        isRtl = true;
      }
      if (neighb.t == mp.tools.SchemePlace.TABLE && neighb.l == mp.tools.SchemePlace.EMPTY ||
          neighb.b == mp.tools.SchemePlace.TABLE && neighb.l == mp.tools.SchemePlace.EMPTY) {
        ang = 0;
        isRtl = true;
      }
      if (neighb.t == mp.tools.SchemePlace.TABLE && neighb.r == mp.tools.SchemePlace.EMPTY ||
          neighb.b == mp.tools.SchemePlace.TABLE && neighb.r == mp.tools.SchemePlace.EMPTY) {
        ang = 0;
        isRtl = false;
      }
      if (neighb.t == mp.tools.SchemePlace.TABLE && neighb.l == mp.tools.SchemePlace.TABLE &&
          neighb.b == mp.tools.SchemePlace.PLACE && neighb.r == mp.tools.SchemePlace.PLACE) {
        ang = 0;
        isRtl = true;
      }
      if (neighb.t == mp.tools.SchemePlace.TABLE && neighb.r == mp.tools.SchemePlace.TABLE &&
          neighb.b == mp.tools.SchemePlace.PLACE && neighb.l == mp.tools.SchemePlace.PLACE) {
        ang = 0;
        isRtl = false;
      }
      if (neighb.b == mp.tools.SchemePlace.TABLE && neighb.l == mp.tools.SchemePlace.TABLE &&
          neighb.t == mp.tools.SchemePlace.PLACE && neighb.r == mp.tools.SchemePlace.PLACE) {
        ang = 0;
        isRtl = true;
      }
      if (neighb.b == mp.tools.SchemePlace.TABLE && neighb.r == mp.tools.SchemePlace.TABLE &&
          neighb.t == mp.tools.SchemePlace.PLACE && neighb.l == mp.tools.SchemePlace.PLACE) {
        ang = 0;
        isRtl = false;
      }

      place.setRotation(ang);
      place.isRtl = isRtl;

      var placeFigure = new goog.graphics.ext.Rectangle(place);
    }

    var caption = new goog.graphics.ext.Group(this.captionGroup_);
    caption.setSize(place.getWidth(), place.getHeight());
    caption.setPosition(place.getLeft(), place.getTop());
    caption.setRotation(place.getRotation());
    caption.isRtl = place.isRtl;
    if (goog.isDef(place.schemePosition))
      caption.schemePosition = place.schemePosition.clone();
    this.placesCaptions_.push(caption);

    placeFigure.setSize(place.getWidth(), place.getHeight());
    placeFigure.setFill(new goog.graphics.SolidFill('#DBEDBE'));
    placeFigure.setStroke(new goog.graphics.Stroke(1, '#CDDEB1'));
  }
};


/**
 *
 */
mp.graphics.Element.prototype.createSitsDom = function() {
  this.labels_ = [];
  for (var i = this.sits_.length - 1; i >= 0; i--) {
    var sit = this.sits_[i];
    if(!sit) continue;

    if (this.isRound_ && goog.isDef(sit.position)) {
      var idx = parseInt(sit.position);
      var place = this.placesCaptions_[idx];
    } else if(goog.isDef(sit.position)) {
      var pos = new goog.math.Coordinate(sit.position.x, sit.position.y);
      var place = null;
      var idx = this.placesCaptions_.length;
      while (idx--) {
        if (goog.math.Coordinate.equals(this.placesCaptions_[idx].schemePosition, pos)) {
          place = this.placesCaptions_[idx];
        }
      }
    }
    if (!! place && !! sit.name) {
      var lbl = this.renderLabel(sit.name,
          new goog.graphics.Font(11, 'Tahoma'),
          new goog.graphics.SolidFill('#863D02', 1),
          place,
          place.isRtl ? 'right' : 'left',
          'center',
          new goog.graphics.SolidFill('#F0C864', 0.7),
          this.isRound_ ? 23 : 3, null, true);
      lbl.mpData = sit;
      this.labels_.push(lbl);
    }
  }
};

/**
 * @return {goog.math.Rect} Bounding rect.
 */
mp.graphics.Element.prototype.getMainObjectRect = function() {
  var rect = new goog.math.Rect(
      this.mainObject_.getLeft(),
      this.mainObject_.getTop(),
      this.mainObject_.getWidth(),
      this.mainObject_.getHeight());

  if (this.mainObject_ instanceof goog.graphics.ext.Shape) {
    var rect = this.mainObject_.getPath().getBoundingBox();
  }

  return rect;
};


/**
 *
 */
mp.graphics.Element.prototype.createCaptionsDom = function() {
  var caption = new goog.graphics.ext.Group(this.captionGroup_),
      rect = this.getMainObjectRect();

  if(this.isTable_ && !this.isRound_) {
    rect = this.scheme_.getBiggestRect(mp.tools.SchemePlace.TABLE);
    var margin = 2;
    var m = this.placeSize_ + margin * 2;
    rect.left *= m;
    rect.top *= m;
    rect.width *= m;
    rect.height *= m;
  }

  caption.setMinWidth(rect.width);
  caption.setMinHeight(rect.height);
  
  caption.setPosition(
      rect.left,
      rect.top);

  this.renderLabel(this.title_,
      new goog.graphics.Font(12, 'Arial'),
      new goog.graphics.SolidFill('white', 1), 
      caption, 'center', 'center',
      null,
      null, true);
};


/**
 *
 */
mp.graphics.Element.prototype.renderLabel = function(
    text, font, textFill, group, 
    opt_align, opt_valign, 
    opt_bgFill, opt_borderRadius, 
    opt_lockW, opt_lockH) {

  if (!goog.isString(text) || !text.length)
    return;

  var margin = 3,
      padding = 3,
      innerW = Math.max(group.getWidth() - margin * 2, 0),
      innerH = Math.max(group.getHeight() - margin * 2, 0),
      tOffset = margin + padding;

  var textMeta = this.graphics_.measureText(text, font, 
      !! opt_lockW ? innerW - padding * 2 : null, 
      !! opt_lockH ? innerH - padding * 2 : null);

  var label = new goog.graphics.ext.Group(group);

  var bg = new goog.graphics.ext.Rectangle(label);
  if (!! opt_bgFill)
    bg.setFill(opt_bgFill);
  if (!!opt_borderRadius && this.getWrapper().getElement()) {
    var impl = this.graphics_.getImplementation();
    impl.setElementAttributes(bg.getWrapper().getElement(),
        {'rx': opt_borderRadius, 'ry': opt_borderRadius});
  }

  var bgWidth = Math.max(innerW, textMeta.width + padding * 2);
  var bgHeight = Math.max(innerH, textMeta.height + padding * 2);

  bg.setSize(bgWidth, bgHeight);
  bg.setPosition(margin, margin);

  if (opt_align == 'center')
    bg.setCenter(group.getWidth() / 2);
  else if (opt_align == 'right') {
    label.setLeft(innerW - bg.getWidth());
  }

  if (opt_valign == 'center')
    bg.setMiddle(group.getHeight() / 2);
  else if (opt_valign == 'bottom') {
    label.setTop(innerH - bg.getHeight());
  }

  for (var i = 0; i < textMeta.lines.length; i++) {
    var impl = label.getGraphicsImplementation(),
        lineOffset = textMeta.baseline * i;
    var txt = impl.drawText(textMeta.lines[i],
        bg.getLeft() + padding,
        bg.getTop() + padding + lineOffset - textMeta.height / 2 + textMeta.baseline / 2,
        bg.getWidth() - padding * 2, bg.getHeight() - padding * 2,
        goog.isDef(opt_align) ? opt_align : 'center',
        goog.isDef(opt_valign) ? opt_valign : 'center',
        font,
        //new goog.graphics.Stroke(0, 'white'),
        null,
        textFill,
        label.getWrapper());
    if(txt.getElement())
      txt.getGraphics().setElementAttributes(txt.getElement(),
          {'text-rendering': 'optimizeSpeed'});
  }

  return label;
};


